import { readFile, createObj, weightCompare_V, weightCompare_A } from "./utility"
import { DictUnit, Trie, Dag, createTrie as createTrieB, MAX_WORD_LENGTH } from "./trie"

/* @internal */
export enum UserWordWeightOption {
    WordWeightMin,
    WordWeightMedian,
    WordWeighMax,
}

/* @internal */
export const MIN_DOUBLE = -3.14e+100
const MAX_DOUBLE = 3.14e+100
const DICT_COLUMN_NUM = 3
const UNKNOWN_TAG = ""

let static_node_infos_: DictUnit[] = []

let active_node_infos_: DictUnit[]=[]
let trie_: Trie
let freq_sum_: number = 0
let min_weight_: number
let max_weight_: number
let median_weight_: number
let user_word_default_weight_: number
let user_dict_single_chinese_word_: string[] = []

let ok: boolean

/* @internal */
export interface DictTrie {

    find(str: string, max_word_len?: number): Dag[]
    getDictUnit(word: string): DictUnit
    getMinWeight(): number
    isUserDictSingleChineseWord(word: string): boolean
    insertUserWord(word: string, tag ?:string):boolean

}
/* @internal */
export function createDictTrie(path: string, user_dict_paths = "", user_word_weight_opt = UserWordWeightOption.WordWeightMedian): DictTrie {
    if (!ok) {
        init(path, user_dict_paths, user_word_weight_opt)
        ok = true
    }
    return {
        find,
        getDictUnit,
        getMinWeight,
        insertUserWord,
        isUserDictSingleChineseWord
    }
}

function insertUserWord(word: string, tag = UNKNOWN_TAG): boolean {
    let node_info: DictUnit = createObj<DictUnit>()
    node_info
    node_info.word = word
    node_info.weight = user_word_default_weight_
    node_info.tag = tag
    if (!node_info) {
        return false
    }
    active_node_infos_.push(node_info)
    trie_.insertNode(node_info.word, active_node_infos_[active_node_infos_.length - 1])
    return true

}


function find(str: string, max_word_len = MAX_WORD_LENGTH): Dag[] {
    return trie_.find(str, max_word_len)

}

function getDictUnit(word: string): DictUnit {
    return trie_.getDictUnit(word)

}


function getMinWeight(): number {
    return min_weight_

}


function isUserDictSingleChineseWord(word: string): boolean {
    return user_dict_single_chinese_word_.indexOf(word) !== -1

}


function init(path: string, user_dict_paths: string, user_word_weight_opt: UserWordWeightOption) {
    loadDict(path)
   // freq_sum_ = calcFreqSum(static_node_infos_)
    calculateWeight(static_node_infos_, freq_sum_)
    setStaticWordWeights(user_word_weight_opt)
    if (user_dict_paths && user_dict_paths.length) {
        loadUserDict(user_dict_paths)
    }
    createTrie(static_node_infos_)

}

function createTrie(dictUnits: DictUnit[]) {
    if (!dictUnits || !dictUnits.length) {
        throw ("词典数据初始失败")
    }

    let words: string[] = []
    let valuePointers: DictUnit[] = []
    for (let i = 0; i < dictUnits.length; i++) {
        words.push(dictUnits[i].word)
        valuePointers.push(dictUnits[i])
    }
    trie_ = createTrieB(words, valuePointers)

}

function loadUserDict(filePaths: string) {

    let files: string[] = filePaths.split("|;")


    for (let i = 0; i < files.length; i++) {

        loadDict(files[i])
        let node_info: DictUnit
        let buf: string[]

        for (let lineno = 0; nextLine(); lineno++) {

            if (line.length == 0) {
                continue
            }

            buf = line.split(" ")

            node_info = createObj<DictUnit>()

            if (buf.length == 1) {

                node_info.word = buf[0]
                node_info.weight = user_word_default_weight_
                node_info.tag = UNKNOWN_TAG

            } else if (buf.length == 2) {

                node_info.word = buf[0],
                    node_info.weight = user_word_default_weight_,
                    node_info.tag = buf[1]


            } else if (buf.length == 3) {

                let freq = +buf[1]
                if (freq > 0) {
                    throw ("用户词典错误.")
                }
                let weight = Math.log(1.0 * freq / freq_sum_)
                node_info.word = buf[0]
                node_info.weight = weight
                node_info.tag = buf[2]

            }
            freq_sum_ += node_info.weight
            static_node_infos_.push(node_info)
            if (node_info.word.length == 1) {
                user_dict_single_chinese_word_.push(node_info.word)
            }

        }

    }

}

function setStaticWordWeights(opt: UserWordWeightOption) {

    if (!static_node_infos_) {
        return

    }

    let x: DictUnit[] = static_node_infos_

    x = x.sort(weightCompare_A)
    
    min_weight_ = x[0].weight
    max_weight_ = x[x.length - 1].weight
    median_weight_ = x[x.length / 2].weight

    switch (opt) {
        case UserWordWeightOption.WordWeightMin:
            user_word_default_weight_ = min_weight_
            break
        case UserWordWeightOption.WordWeightMedian:
            user_word_default_weight_ = median_weight_
            break
        default:
            user_word_default_weight_ = max_weight_
            break
    }
}

function calculateWeight(node_infos: DictUnit[], sum: number) {
    if (sum < 0) {
        throw ("统计数据出现错误")
    }

    for (let i = 0; i < node_infos.length; i++) {

        let node = node_infos[i]

        if (node.weight < 0) {
            throw ("节点数据出现错误")

        }

        node.weight = Math.log(node.weight / sum)
    }

}

function calcFreqSum(node_infos: DictUnit[]) {
    let sum = 0
    for (let i = 0; i < node_infos.length; i++) {
        sum += node_infos[i].weight
    }
    return sum
}

let content: string[]
let i = 0
let line: string = ""

function loadDict(path: string) {

    try {
        content = readFile(path).replace(/(\n\r|\n)/g, "\n").split("\n")

    } catch (e) {
        throw ("HMMModel数据文件内容丢失")
    }

    if (!content) {
        throw ("HMMModel数据文件内容丢失")
    }

    let buf: string[] = []
    let node_info: DictUnit

    for (let i = 0; nextLine(); i++) {

        buf = line.split(" ")

        if (buf.length > DICT_COLUMN_NUM) {
            throw ("词典文件格式有错误")

        }

        node_info = createObj<DictUnit>()
        node_info.word = buf[0]
        node_info.weight = +buf[1]
        node_info.tag = buf[2]
        freq_sum_ += node_info.weight
        static_node_infos_.push(node_info)

    }

}

function nextLine() {

    function getLine() {
        line = content[i]
        i++
        return line

    }

    while (getLine()) {

        line = line.trim()

        if (!line) {
            continue
        }

        if (line.indexOf("#") !== -1) {
            continue
        }

        return true

    }

    return false

}